# Create your views here7.

try:
	import cPickle as pickle
except ImportError:
	import pickle

import pdb


import pprint
pp = pprint.PrettyPrinter( indent = 4 )


from random import choice

from urllib2 import urlopen
from svaha.apireq import ApiRequest
from svaha.views import json, template
from svaha.settings import USERS_ROOT, APP_NAME, APP_ID
from svaha.utils import debug_utf8 as debug
import os
from app.models import Settings, DatingPhrase, Search, SearchOffset, SavedScore, AdvBlock, DevMessage, Visitor
from django.db import transaction
from django.db.models import Count

import json as jsoncoder
from itertools import chain

from django.db.models import F

_CRIT_MESSAGE_COUNT = 10

#_get_contact_info

from sklearn import svm
from vectors import convert_props_to_ints, convert_lookfor

class WrongSumError( Exception ):
	pass

@template( 'admin-dates.html' )
def dates( request, user_id ):
	v = Visitor.objects.filter( user_id = user_id ).order_by( '-dt' )
	u = v[ 0 ]
	return request, { 'visits': v, 'user2': u }

@template( 'admin-visitors.html' )
def visitors( request ):
	" Returns visitors ids, count grouped by user_id "
	visitors = Visitor.objects.values( 'user_id' ).annotate( count = Count( 'user_id' ) ).order_by( '-count' )

	return request, { 'visitors': visitors }

@json
def pay( request ):
	"""	{	
			u'extra': u'from_id_699728001', 
			u'oid': u'699728001', 
			u'app_id': u'789', 
			u'validation_id': u'162030146', 
			u'amount': u'1.00000', 
			u'sig': u'4c566872ed6cc2d41789060a6a7e64c9', 
			u'time': u'1376545094', 
			u'operation_id': u'277835854', 
			u'amount_developer': u'0.68072'
		}
	"""

	AdvBlock.set( req[ 'oid' ] )
	
	"""req = request.REQUEST

	user_id = req[ 'oid' ]
	app_id = req[ 'app_id' ]

	if app_id == APP_ID:
		if int( float( req[ 'amount' ] ) ) == 1:
			AdvBlock.set( user_id )
		else:
			raise WrongSumError( 'User payed a wrong sum' )				"""

	return request, { 'result': "OK" }

@json
def send_dev_message( request ):
	req = request.REQUEST

	user_id = req[ 'oid' ]
	message = req[ 'message' ]

	DevMessage.objects.create( user_id = user_id, message = message )

	return request, { 'result': "OK" }


@json
def add_to_my_phrases( request ):
	req = request.REQUEST
	user_id = req[ 'oid' ]	
	
	DatingPhrase(	phrase = req[ 'phrase' ],
					user_id = user_id,
					companion_sex = req[ 'look_for' ].lower(),
					_type = 'c' ).save()
	
	return request, { 'result': "OK" }


@json
def save_my_phrase( request ):
	" Saves user's phrase "

	req = request.REQUEST

	user_id = req[ 'oid' ]

	phrase = req[ 'phrase' ]

	
	if phrase != '':
		DatingPhrase(	phrase = req[ 'phrase' ],
						user_id = user_id,
						companion_sex = req[ 'look_for' ].lower(),
						_type = 'c' ).save()
		d = { 'result': "OK" }
	else:
		d = { 'result': "ERROR_EMPTY_PHRASE" }
							
	return request, d

@template( 'test.html' )
def test( request ):
	" Test anything we want. "
	return request, { 'param': "test" }

@template( 'test-session.html' )
def test_session( request ):
	test = request.session.get( 'test', "unknown" )
	request.session[ 'test' ] = "22"

	return request, { 'test': test }

def get_adv_block( sid, user_id ):	
	api_req = ApiRequest( APP_ID, user_id, sid )
	anketa_oid = AdvBlock.get()

	if anketa_oid != None:
		user_data = api_req.get_anketas( [ str( anketa_oid ) ] )[ 'data' ][ 0 ]
		user_info = user_data[ 'info' ]
		result = { 'anketa_link': user_info[ 'anketa_link' ], 'photo_url': user_info[ 'medium_photo_url' ] }
	else:
		result = None

	return result		

@json
def get_user_random_phrase( request ):
	phrase = DatingPhrase.get_user_random()

	return	request,	{
							'phrase': phrase.phrase,
							'user_id': phrase.user_id
						}

@json
def get_dating_phrase( request ):
	
	req = request.REQUEST

	user_id = req[ 'oid' ]

	companion_sex = req[ 'companion_sex' ]
	my_name = req[ 'my_name' ]
	companion_name = req[ 'companion_name' ]
	type_ = req[ 'type' ] # type of phrase: usual/sexy

	if type_ == 'u':
		phrase = DatingPhrase.get_random( companion_sex, my_name, companion_name, 'u' )		
	elif type_ == 'x':
		phrase = DatingPhrase.get_random( companion_sex, my_name, companion_name, 'x' )		
		"""else:
			phrase = None"""
	elif type_ == 'my':
		# my
		phrase = DatingPhrase.get_random( companion_sex, my_name, companion_name, 'c', user_id )		
		# user phrase
	elif type_ == 'c':
		# any user
		phrase = DatingPhrase.get_random( companion_sex, my_name, companion_name, 'c' )		
	else:
		phrase = None
	
	return request, {	'phrase': phrase if phrase != None else "null" }

@template( "test-predict.html" )
def test_predict( request ):
	
	req = request.REQUEST

	my_oid = req[ 'my_oid' ]
	anketa_oid = req[ 'anketa_oid' ]
	sid = req[ 'sid' ]
	
	api_req = ApiRequest( APP_ID, my_oid, sid )
	data = api_req.get_anketas( [ anketa_oid ] )[ 'data' ][ 0 ]

	clf = _get_clf( my_oid )

	mark = _predict( clf, [ data ] )[ 0 ]

	return request, { 'mark': mark }

@template( "empty.html" )
def my_info( request ):	
	user_id = req[ 'oid' ]
	sid = req[ 'sid' ]

	api_req = ApiRequest( APP_ID, user_id, sid )

	return request, {}
	

@json
def predict( request ):
	req = request.REQUEST

	user_id = req[ 'oid' ]
	sid = req[ 'sid' ]

	api_req = ApiRequest( APP_ID, user_id, sid )

	offset_obj = SearchOffset.objects.get( user_id = user_id )

	iam = req[ 'iam' ]
	look_for = req[ 'look_for' ]

	if 'inc_offset' in req:
		if req[ 'inc_offset' ] == "true":
			offset_obj.offset += _OFFSET_STEP
			offset_obj.save()

	search_list = [	
					offset_obj.offset, 
					iam, 
					look_for, 
					req[ 'min_age' ], 
					req[ 'max_age' ],
					req[ 'country_id' ], 
					req[ 'region_id' ]  
	]

	if req[ 'is_city_id' ] == "true":
		search_list.append( req[ 'city_id' ] )

	offset = SearchOffset.objects.get( user_id = user_id ).offset

	result = api_req.search( *search_list )
	data = result[ 'data' ]
	users = data[ 'users' ]
		
	if users != None:		
		num_ancets = data[ 'limit' ]
	
		clf = _get_clf( user_id )
	
		out_users = []

		is_limit_exceeded = False # there are more ancets		
			
		for user in users:
			try:
				proba = _predict( clf, [ user ] )
			except:
				raise			
			out_users.append( { 'data': user, 'proba': proba } )
	else:
		out_users = None
		is_limit_exceeded = True # there are NO more ancets
	
	return request, {	'is_limit_exceeded': "true" if is_limit_exceeded else "false",
						'users': out_users,
						'adv_block': get_adv_block( sid, user_id )  }

num_photos_on_screen = 5
_OFFSET_STEP = 10

@json
def set_notified_wrong_probas( request ):
	req = request.REQUEST
	user_id = req[ 'oid' ]
	Settings.objects.filter( user_id = user_id ).update( notified_wrong_probas = True )


@template( "4.html" )
def view4( request ):

	req = request.REQUEST

	user_id = req[ 'oid' ]
	sid = req[ 'sid' ]

	api_req = ApiRequest( APP_ID, user_id, sid )

	my_ancet = api_req.get_anketas( [ str( user_id ) ] )[ 'data' ][ 0 ]

	search = Search.objects.get( user_id = user_id )
	settings, p = Settings.objects.get_or_create( user_id = user_id )

	out = {	
			'num_photos_on_screen': num_photos_on_screen, 
			'screen_photos_range': range( 0, num_photos_on_screen ), 
			'sid': sid, 
			'app_id': APP_ID,
			'user_id': user_id,
			'iam': my_ancet[ 'info' ][ 'gender' ],
			'look_for': search.sex.capitalize(),
			'my_name': my_ancet[ 'info' ][ 'name' ],
			'min_age': search.min_age,
			'max_age': search.max_age,
			'country_id': my_ancet[ 'location' ][ 'country_id' ],
			'region_id': my_ancet[ 'location' ][ 'region_id' ],
			'notified_wrong_probas': "true" if settings.notified_wrong_probas else "false",
			'adv_block': get_adv_block( sid, user_id )
	}

	# update city if needed
	if search.live_in == 'c':
		out.update( { 'city_id': my_ancet[ 'location' ][ 'city_id' ] } )

	# create SearchOffset
	SearchOffset.objects.get_or_create( user_id = user_id )

	settings.go_to_last_page = True
	settings.save()
	
	return request, out


@json
def save_search( request ):
	req = request.REQUEST
	
	user_id = int( req[ 'oid' ] ) 
	sid = req[ 'sid' ]		
	
	keys = [ 'min_age', 'max_age', 'live_in', 'sex' ]
		
	data = {}	
	for key in keys:
		data[ key ] = req[ key ]
	
	Search.objects.filter( user_id = user_id ).update( **data )	
	
	return request, { 'result': "OK" }

@template( '3.html' )
def view3( request ):

	req = request.REQUEST

	user_id = int( req[ 'oid' ] )
	sid = req[ 'sid' ]
	is_limit_exceeded = True if 'is_limit_exceeded' in req and req[ 'is_limit_exceeded' ] == "true" else False

	api_req = ApiRequest( APP_ID, user_id, sid )
	my_ancet = api_req.get_anketas( [ str( user_id ) ] )[ 'data' ][ 0 ]

	my_age = my_ancet[ 'info' ][ 'age' ]
	
	my_sex = 'm' if my_ancet[ 'info' ][ 'gender' ] == 'M' else 'f'

	search = Search.save_default_params( user_id, my_age, my_sex )	

	SearchOffset.reset( user_id = user_id ) # set offset to 0

	return request, {	'is_limit_exceeded': is_limit_exceeded,
						'range_min_age': Search.RANGE_MIN_AGE, 
						'range_max_age': Search.RANGE_MAX_AGE, 
						'min_age': search.min_age,
						'max_age': search.max_age,
						'sex': search.sex,
						'live_in': search.live_in }


@json
def train_switch( request ):

	#pdb.set_trace()

	req = request.REQUEST

	user_id = req[ 'oid' ]
	sid = req[ 'sid' ]

	api_req = ApiRequest( APP_ID, user_id, sid )

	req = request.REQUEST
	user_id = req[ 'oid' ]

	contact_ids_score0 = [ x for x in req.getlist( 'contact_ids_score0[]' ) ]
	contact_ids_score1 = [ x for x in req.getlist( 'contact_ids_score1[]' ) ]

	# save scores
	SavedScore.save_cids( user_id, contact_ids_score0, contact_ids_score1 )

	contacts_score0 = api_req.get_anketas( contact_ids_score0 )[ 'data' ]
	contacts_score1 = api_req.get_anketas( contact_ids_score1 )[ 'data' ]

	# exclude off contacts		

	_train( user_id, contacts_score0, contacts_score1 )
	
	return request, { 'result': "OK" }

class NotEnoughContacts( Exception ):
	pass


def _scores_by_msgs( msgs, s_by_ids ):
	"""	Returns a tuple of successful and unsuccessfull contacts
		s_by_ids is [ [ id1_with_score0, id2_with_score0 ], [ id1_with_score1, id2_with_score2 ] ] (it's saved scores)
	"""
	
	if len( msgs ) < 2:
		raise NotEnoughContacts( 'Less than 2 contacts to make scores.' )
	
	score1 = []
	score0 = []

	contacts = sorted( msgs.iteritems(), key = lambda x: -x[ 1 ][ 0 ] )

	# 3 contacts are success

	clen = len( contacts )
	success_clen = int( clen * 0.3 )

	if success_clen > 3:
		success_clen = 3		
	
	score1 = []	
	for i in range( 0, success_clen ):
		id_ = contacts[ 0 ][ 0 ]
		append_item = [ id_, contacts[ 0 ][ 1 ][ 1 ], contacts[ 0 ][ 1 ][ 2 ] ]

		if id_ not in s_by_ids[ 0 ]:
			score1.append( append_item )
		else:
			score0.append( append_item )
		del contacts[ 0 ]
	
	contacts.reverse()

	clen_ = len( contacts )
	unsuccess_clen = 10
	if unsuccess_clen > clen_:
		unsuccess_clen = clen_

	score0 = []
	for i in range( 0, unsuccess_clen ):
		id_ = contacts[ i ][ 0 ]
		append_item = [ id_, contacts[ i ][ 1 ][ 1 ], contacts[ i ][ 1 ][ 2 ] ]

		if id_ not in s_by_ids[ 1 ]:
			score0.append( append_item )	
		else:
			score1.append( append_item )
	
	return score0, score1

@template( "2.html" )
def view2( request ):
	req = request.REQUEST
	user_id = req[ 'oid' ]
	sid = req[ 'sid' ]

	api_req = ApiRequest( APP_ID, user_id, sid )
	api_contacts = api_req.get_contact_list()[ 'data' ][ 'contacts' ]

	msgs = { int( x[ 'info' ][ 'oid' ] ): ( x[ 'message_count' ], x[ 'info' ][ 'medium_photo_url' ], x[ 'info' ][ 'anketa_link' ] ) for x in api_contacts }		

	saved_scores_by_ids = SavedScore.get_cids( user_id = user_id )

	d = { 'app_name': APP_NAME }
	
	try:
		scores = _scores_by_msgs( msgs, saved_scores_by_ids )
	except NotEnoughContacts:
		d.update( not_enough_contacts = True )
	else:
		d.update( not_enough_contacts = False )
		d.update( scores = scores )
	return request, d


@template( "index.html" )
def index( request ):
	req = request.REQUEST

	if 'sid' in req and 'oid' in req:
		sid = req[ 'sid' ]
		user_id = req[ 'oid' ]

		Visitor.visit( user_id )
	
		try:
			go_to_last_page = Settings.objects.get( user_id = user_id ).go_to_last_page
		except Settings.DoesNotExist:
			go_to_last_page = False
		
		if go_to_last_page:
			page = 4
		else:
			page = 2
	
		result = ( request, { 'page': page, 'sid': sid, 'user_id': user_id } )
	else:
		result = ( request, None )
	
	return result

@template( "1.html" )
def view1( request ):

	req = request.REQUEST

	return request, { 'CRIT_MESSAGE_COUNT': _CRIT_MESSAGE_COUNT }


def _classify_by_message_cnt( message_cnt ):
	if message_cnt < _CRIT_MESSAGE_COUNT:
		result = 0
	else:
		result = 1
	return result

def _append_to_file( s ):
	with open( "predict.txt", "a" ) as f:
		f.write( s )

def _cls_contacts( contacts ):
	" Get classified contacts. "
	
	contacts_scores = []
	index = 0
	for contact in contacts:
		contact_data = contact.get_data()
		contacts_scores.append(	[ \
								contact, 
								_classify_by_message_cnt( int( contact_data[ 'message_count' ] ) ),
							]
		)

		index += 1
	
	return contacts_scores



#methods retrieves ankets and train the model
def _train( oid, contacts_score0, contacts_score1 ):
	
	# add classifier retrieve here
	# pickle.

	file_path = os.path.join( USERS_ROOT, str( oid ) )

	clf = _new_clf()
	
	# remove contacts we have handled
	index = 0
	
	train_contacts = []

	x = []
	y = []
			
	for contact in contacts_score0:
		# get a contact with oid
		x.append( convert_props_to_ints( contact ) )
		y.append( 0 )

	for contact in contacts_score1:
		# get a contact with oid
		x.append( convert_props_to_ints( contact ) )
		y.append( 1 )	
	
	clf = clf.fit( x, y )
		
	with open( file_path, 'w' ) as f:
		pickle.dump( clf, f )
		
	return clf

def _new_clf():
	return svm.SVC( kernel = 'linear', probability = True )

def _get_clf( oid ):
	file_path = os.path.join( USERS_ROOT, str( oid ) )
	if os.path.exists( file_path ):
		with open( file_path, 'r' ) as f:
			clf = pickle.load( f )
	else:
		clf = _new_clf()

	return clf
	

def _predict( clf, contacts ):
	result = []

	for contact in contacts:
		conv_contact = convert_props_to_ints( contact )


		proba_obj = clf.predict_proba( conv_contact )
		result.append( int( round( proba_obj[ 0 ][ 1 ], 2 ) * 100 ) )

	return result

# admin
@json
def num_users( request ):
	result = Search.objects.all().count()
	return { 'num_users': result }